package ru.spb.osll.myfit.server;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Properties;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;

import ru.spb.osll.myfit.common.TextParsingTools;

public class Configurator {
  private Logger log = Logger.getLogger(getClass());
  private static final String JAR_FILE = ".jar";
  private static final String CONFIG_FILE = "../conf/config.xml";
//  private static final String CONFIG_PROPS_FILE = "../conf/config.properties";
  private static final String LOGGER_CONFIG_FILE = "../conf/testlogger.properties";
  private static final int DEFAULT_LOG_TRANSFER_SIZE = 2048;

  private final Properties testLogProperties = new Properties();

  private String m_dependenciesDir;
  private String m_fixtureDir;
  private String m_rootDir;
  private String m_testResultsDir;
  private String m_suitesDir;
  private String m_suiteResultsDir;
  private String m_resourceDir;
  private int m_logTransferSize;
  private boolean m_copyDependencies;
  private boolean m_deleteDependencies;

  private static Configurator instance = null;

  protected Configurator() {
    this(CONFIG_FILE);
  }

  protected Configurator(String configFile) {
   if(configFile.endsWith(".xml")) {
     loadXMLConfig(configFile);
   } else {
     loadPropertyConfig(configFile);
   }
  }

  public static Configurator getInstance() {
    if (instance == null) {
      instance = new Configurator();
    }
    return instance;
  }

  public static void createInstanceWithCustomConfigFile(String configFile) {
    if (instance == null) {
      instance = new Configurator(configFile);
    }
  }
  
  private void loadXMLConfig(String fileName) {
    try {
      DocumentBuilder xml = DocumentBuilderFactory.newInstance().newDocumentBuilder();

      log.info("Configurator: start parsing config.xml");

      Document doc = xml.parse(new File(fileName));
      loadConfigFile(doc);
      loadLogProperties(LOGGER_CONFIG_FILE);
    } catch (Exception e) {
      log.error(" Parsing config.xml error", e);
    }
  }

  public void loadConfigFile(Document doc) {
    XPathFactory factory = XPathFactory.newInstance();
    m_dependenciesDir = getConfigValue(doc, factory, "//doc/jars/@path", "");
    m_fixtureDir = getConfigValue(doc, factory, "//doc/fixtures/@path", "");
    m_rootDir = getConfigValue(doc, factory, "//doc/root/@path", "");
    m_testResultsDir = getConfigValue(doc, factory, "//doc/execresults/@path", "");
    m_suitesDir = getConfigValue(doc, factory, "//doc/suites/@path", "");
    m_suiteResultsDir = getConfigValue(doc, factory, "//doc/suiteexecresults/@path", "");
    m_resourceDir = getConfigValue(doc, factory, "//doc/resources/@path", "");
    m_logTransferSize = valueToInt(getConfigValue(doc, factory, "//doc/logtransfersize/@value", ""), DEFAULT_LOG_TRANSFER_SIZE);
    m_copyDependencies = valueToBoolean(getConfigValue(doc, factory, "//doc/copyDependencies/@value", ""), true);
    log.info("m_copyDependencies: " + m_copyDependencies);
    m_deleteDependencies = valueToBoolean(getConfigValue(doc, factory, "//doc/deleteDependencies/@value", ""), true);
    log.info("m_deleteDependencies: " + m_deleteDependencies);
    log.info("Configurator: finish");
  }
  
  public void loadPropertyConfig(String filename) {
    FileInputStream stream = null;
    try {
      Properties props = new Properties();
      stream = new FileInputStream(filename);
      props.load(stream);
      m_dependenciesDir = props.getProperty("jars", "");
      m_fixtureDir = props.getProperty("fixtures", "");
      m_rootDir = props.getProperty("root", "FitNesseRoot");
      m_testResultsDir = props.getProperty("execresults", "testResults");
      m_suitesDir = props.getProperty("suites");
      m_suiteResultsDir = props.getProperty("suiteexecresults");
      m_resourceDir = props.getProperty("resources");
      m_logTransferSize = valueToInt(props.getProperty("logtransfersize", ""), DEFAULT_LOG_TRANSFER_SIZE);
      m_copyDependencies = valueToBoolean(props.getProperty("copyDependencies", ""), true);
      log.info("m_copyDependencies: " + m_copyDependencies);
      m_deleteDependencies = valueToBoolean(props.getProperty("deleteDependencies", ""), true);
      log.info("m_deleteDependencies: " + m_deleteDependencies);
    } catch (IOException e) {
      log.error("error parsing " + filename, e);
    } finally {
      if(stream != null) {
        try {
          stream.close();
        } catch (IOException ignore) {
       
        }
      }
    }
    log.info("Configurator: finish");
  }

  private String getConfigValue(Document doc, XPathFactory factory, String xpathStr, String defaultValue) {
    try {
      XPath xpath = factory.newXPath();
      XPathExpression expr = xpath.compile(xpathStr);
      String res = expr.evaluate(doc);
      return res;
    } catch (XPathExpressionException e) {
      log.error("failed to get configuration value " + xpathStr, e);
    }
    return defaultValue;
  }

  private int valueToInt(String value, int defaultValue) {
    try {
      return Integer.valueOf(value);
    } catch (NumberFormatException e) {
      log.error("failed to get convert value " + value, e);
    }
    return defaultValue;
  }

  private boolean valueToBoolean(String value, boolean defaultValue) {
    if(value.toLowerCase().equals("true")) {
      return true;
    } else if (value.toLowerCase().equals("false")) {
      return false;
    } else {
      return defaultValue;
    }
  }

  private void loadLogProperties(String fileName) {
    try {
      FileInputStream inputStream = new FileInputStream(fileName);
      testLogProperties.load(inputStream);
    } catch (FileNotFoundException e) {
    } catch (IOException e) {
    }
  }

  public Properties getLoggerProperties() {
    return testLogProperties;
  }

  public String getFixtureDir() {
    return m_fixtureDir;
  }

  public String getDependenciesDir() {
    return m_dependenciesDir;
  }

  public String getRootDir() {
    return m_rootDir;
  }

  public String getTestResultsDirectory() {
    return m_testResultsDir;
  }

  public String getResources() {
    return m_resourceDir;
  }

  public String getSuitesDir() {
    return m_suitesDir;
  }
  
  public String getSuiteResultsDirectory() {
    return m_suiteResultsDir;
  }

  public int getLogTransferSize() {
    return m_logTransferSize;
  }
  
  public boolean getCopyDependencies() {
    return m_copyDependencies;
  }
  
  public boolean getDeleteDependencies() {
    return m_deleteDependencies;
  }

  public String getPathDeclaration() {
    File dir = new File(getDependenciesDir());
    StringBuffer result = new StringBuffer();

    String[] jars = dir.list(new FilenameFilter() {
      @Override
      public boolean accept(File dir, String name) {
        return name.endsWith(JAR_FILE);
      }
    });

    result.append(TextParsingTools.PATH);
    for (String str : jars) {
      try {
        result.append(new File(getDependenciesDir(), str).getCanonicalPath() + TextParsingTools.PATH_SEPARATOR);
      } catch (IOException e) {
        result.append(new File(getDependenciesDir(), str).getAbsolutePath() + TextParsingTools.PATH_SEPARATOR);
      }
    }
    result.append(TextParsingTools.CR_and_NEL);

    return result.toString();
  }

}
